package com.jt.aop;

import com.jt.annotation.CacheFind;
import com.jt.exception.ServiceException;
import com.jt.utils.ObjectMapperUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import redis.clients.jedis.JedisCluster;

/**
 * Redis缓存切面
 * 切面 = 切入点表达式+通知
 * @author dc
 * @Classname RedisAspect
 * @Date 2020/2/18 14:11
 */
@Component
@Aspect
@Slf4j
public class RedisAspect {

    @Autowired
    private JedisCluster jedis;

    private  static final Logger logger= LoggerFactory.getLogger(RedisAspect.class);

    /**
     * 1.准备key->包名.类名.方法名::第一个参数(用户指定的key),value: java对象->json->字符串 redis
     * 2.查询缓存
     * 1)没有数据,执行查询数据库操作(执行目标方法),将目标方法返回值转换为json串,存入redis中
     * 2)有数据,动态获取缓存数据之后利用工具API转化为真实的对象(对象类型由MethodSignature接口中方法获取)
     * @param joinPoint 连接点
     * @param cacheFind 自定义注解
     * @return 查询到的目标方法结果
     */
    @Around("@annotation(cacheFind)")
    public Object around(ProceedingJoinPoint joinPoint, CacheFind cacheFind){
        long startTime = System.currentTimeMillis();
        String key = getKey(joinPoint,cacheFind);
        Object obj;
        String value = jedis.get(key);
        try {
            if (StringUtils.isEmpty(value)) {
                obj = joinPoint.proceed();
                String json = ObjectMapperUtil.toJson(obj);
                if (cacheFind.second() > 0) {
                    jedis.setex(key, cacheFind.second(), json);
                } else {
                    jedis.set(key, json);
                }
                long dbTime = System.currentTimeMillis();
                logger.info("访问数据库时间:"+(dbTime-startTime));
                return obj;
            }else{
                Class<?> targetClass = getTargetClass(joinPoint);
                String json = jedis.get(key);
                obj = ObjectMapperUtil.toObject(json, targetClass);
                long cacheTime = System.currentTimeMillis();
                logger.info("访问redis缓存时间:"+(cacheTime-startTime));
                return obj;
            }
        }
        catch (Throwable throwable) {
            throwable.printStackTrace();
            throw new ServiceException("执行目标方法失败");
        }
    }

    /**
     * 获取目标方法返回值类型
     * @param joinPoint 连接点信息
     * @return 方法返回值类型
     */
    private Class<?> getTargetClass(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        return signature.getReturnType();
    }

    /**
     * 获取key值
     * @param joinPoint 连接点信息
     * @param cacheFind 缓存注解
     * @return 定义redis的key值
     */
    private String getKey(ProceedingJoinPoint joinPoint, CacheFind cacheFind) {
        String key = cacheFind.key();
        if(!StringUtils.isEmpty(key)){
            return key;
        }else{
            //包名.类名.方法名::第一个参数
            String className = joinPoint.getSignature().getDeclaringTypeName();
            String methodName = joinPoint.getSignature().getName();
            Object arg = joinPoint.getArgs()[0];
            key = className+"."+methodName+"::"+arg;
            return key;
        }
    }

}
